/*
 * Copyright Lluis Pamies i Juarez <lluis(at)pamies.cat>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You can get the full license at:
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 */

/*
 * Auxiliar functions
 */

function createMethodReference(object, methodName) {
    return function () {
        return object[methodName].apply(object, arguments);
    };
}

function int(arg)
{
    return arg-(arg%1)
}

/*
 * Drag and Drop class
 */

function DragDrop(event)
{
        this.x_ini = event.screenX
        this.y_ini = event.screenY
        this.x_inc = 0
        this.y_inc = 0
}

DragDrop.prototype.MouseMovement = function(event)
{
        this.x_inc = event.screenX - this.x_ini
        this.y_inc = event.screenY - this.y_ini
        this.x_ini = event.screenX
        this.y_ini = event.screenY
}

DragDrop.prototype.GetMovementX = function()
{
        val = this.x_inc
        this.x_inc = 0
        return val
}

DragDrop.prototype.GetMovementY = function()
{
        val = this.y_inc
        this.y_inc = 0
        return val
}

/*
 * Main map class
 */

function Map(elem)
{
    //The size of one piece
    MapPiece.size = 256
    
    //Initailize objects
    this.drag = null
    this.zoom = 1
    this.resource = null
    this.map = document.createElement('div')
    elem.appendChild(this.map)
    this.InitializeMap()

    this.width = this.map.offsetWidth
    this.height = this.map.offsetHeight

    //Set pool of pieces
    this.pool_height = 2*MapPiece.size*(int(this.height/MapPiece.size)+1)
    this.pool_width = 2*MapPiece.size*(int(this.width/MapPiece.size)+1)
    this.pool_num_x = this.pool_width/MapPiece.size
    this.pool_num_y = this.pool_height/MapPiece.size
    this.length = this.pool_num_x * this.pool_num_y

    // Create the images
    this.pieces = new Array(this.length)
    for(j=0; j<this.pool_num_y; j++)
        for(i=0; i<this.pool_num_x; i++)
            this.pieces[j*this.pool_num_x+i] = new MapPiece(this,i,-j)

    // Link the images
    for(j=0; j<this.pool_num_y; j++)
        for(i=0; i<this.pool_num_x; i++) {
            index = (j*this.pool_num_x)+i
            if(index==0) left = this.pool_num_x-1
            else     left = (index-1)%this.pool_num_x
            this.pieces[index].left = this.pieces[left]
            this.pieces[index].right = this.pieces[(index+1)%this.pool_num_x]
        
            this.pieces[index].up = this.pieces[(index+this.length-this.pool_num_x)%this.length]
            this.pieces[index].down = this.pieces[(index+this.pool_num_x)%this.length]
        }

    // Show the images
    for(j=0; j<this.pool_num_y; j++) {
        for(i=0; i<this.pool_num_x; i++) {
            piece = this.pieces[(j*this.pool_num_x)+i]
            piece.SetPos(MapPiece.size*i,MapPiece.size*j)
            piece.Show()
        }
    }
    
    this.map.style.cursor = '-moz-grab'
    this.map.onmousedown = createMethodReference(this,'MouseDown')
    this.map.onmousemove = createMethodReference(this,'MouseMove')
    document.onmouseup = createMethodReference(this,'MouseUp')

    this.zoom_in = document.createElement('div')
    this.zoom_in.onclick = createMethodReference(this,'ZoomIn')
    this.zoom_in.style['position'] = 'absolute'
    this.zoom_in.style['cursor'] = 'pointer'
    this.zoom_in.style['width'] = '22px'
    this.zoom_in.style['height'] = '22px'
    this.zoom_in.style['zIndex'] = '200'
    this.zoom_in.style['top'] = '5px'
    this.zoom_in.style['left'] = '5px'
    this.zoom_in.style['border'] = '1px solid gray'
    this.zoom_in.style['backgroundColor'] = 'white'
    this.zoom_in.style['backgroundImage'] = 'url(\'in.png\')'
    elem.appendChild(this.zoom_in)

    this.zoom_out = document.createElement('div')
    this.zoom_out.onclick = createMethodReference(this,'ZoomOut')
    this.zoom_out.style['position'] = 'absolute'
    this.zoom_out.style['cursor'] = 'pointer'
    this.zoom_out.style['width'] = '22px'
    this.zoom_out.style['height'] = '22px'
    this.zoom_out.style['zIndex'] = '200'
    this.zoom_out.style['top'] = '5px'
    this.zoom_out.style['left'] = '32px'
    this.zoom_out.style['border'] = '1px solid gray'
    this.zoom_out.style['backgroundColor'] = 'white'
    this.zoom_out.style['backgroundImage'] = 'url(\'out.png\')'
    elem.appendChild(this.zoom_out)
}
 
Map.prototype.Update = function()
{
    if(this.resource) {
        for(j=0; j<this.pool_num_y; j++) {
            for(i=0; i<this.pool_num_x; i++) {
                this.pieces[j*this.pool_num_x+i].Load()
            }
        }
    }
}

Map.prototype.Display = function(map_resource)
{
    this.zoom = map_resource.ini_zoom
    this.resource = map_resource
    this.Update()
}


Map.prototype.ZoomIn = function()
{
    var zoom = (this.zoom/this.resource.ini_zoom)/2
    var scale = this.resource.default_scale/zoom
    if(this.resource.min_scale<scale) {
        this.zoom = zoom*this.resource.ini_zoom
        this.Update()
    }
}

Map.prototype.ZoomOut = function()
{
    var zoom = (this.zoom/this.resource.ini_zoom)*2
    var scale = this.resource.default_scale*zoom
    if(this.resource.max_scale>scale) {
        this.zoom = zoom*this.resource.ini_zoom
        this.Update()
    }
}

/*
 * Sets the map DIV properties
 */
Map.prototype.InitializeMap = function()
{
    this.map.style.top = '0px'
    this.map.style.bottom = '0px'
    this.map.style.left = '0px'
    this.map.style.right = '0px'
    this.map.style.position = 'absolute'
    this.map.style.overflow = 'hidden'
    this.map.style.zIndex = '10'
}

/*
 * Add to all pieces the drag increment, to scroll the map
 */
Map.prototype.Scroll = function()
{
    x = this.drag.GetMovementX()
    y = this.drag.GetMovementY()
    for(i=0; i<this.length; i++) {
        this.pieces[i].Scroll(x,y)
    }
    
    //Refresh the positions of the overlopped images
    for(i=0; i<this.length; i++) {
        this.pieces[i].Refresh()
    }
}

Map.prototype.MouseDown = function(event)
{
    e = event || window.event
    this.drag = new DragDrop(e)
    this.map.style.cursor = 'move'
    return false
}

Map.prototype.MouseUp = function(event)
{
    e = event || window.event
    this.drag = null
    this.map.style.cursor = '-moz-grab'
}

Map.prototype.MouseMove = function(event)
{
    e = event || window.event
    if(this.drag != null){
        this.drag.MouseMovement(e)
        this.Scroll()
        return false
    }
}

/*
 * MapPiece class
 */

function MapPiece(parent,x,y)
{
    //Map properties
    this.map_x = x
    this.map_y = y

    //Initializations
    this.temp = new Image()
    this.temp.onload = createMethodReference(this, "OnLoad")

    this.img = new Image()
    this.parent = parent
    this.parent.map.appendChild(this.img)

    this.img.style.position = 'absolute'
    this.img.style.visibility = 'hidden'
    this.img.style.MozUserSelect = 'none'
    this.img.style.width = MapPiece.size;
    this.img.style.height = MapPiece.size;
    this.img.style.zIndex = 5;
    this.img.src = 'wait.gif'

    //Screen coordinates    
    this.x = null
    this.y = null

    //Linked nodes
    this.left  = null 
    this.right = null
    this.up    = null
    this.down  = null
}

MapPiece.prototype.SetPos = function(x,y)
{
    this.x = x
    this.y = y
    this.img.style.left = x
    this.img.style.top = y
}

MapPiece.prototype.Scroll = function(x,y)
{
    this.x += x
    this.y += y
    this.img.style.left = this.x
    this.img.style.top = this.y
}

MapPiece.prototype.Refresh = function()
{
    //Control X
    if(this.x < -MapPiece.size){
        this.x = this.left.x + MapPiece.size
        this.map_x = this.left.map_x + 1;
        this.Load()
    }else if(this.x > (this.parent.pool_width-MapPiece.size)){
        this.x = this.right.x - MapPiece.size
        this.map_x = this.right.map_x - 1;
        this.Load()
    }
    
    //Control Y
    if(this.y < -MapPiece.size){
        this.y = this.up.y + MapPiece.size
        this.map_y = this.up.map_y - 1;
        this.Load()
    }else if(this.y > (this.parent.pool_height-MapPiece.size)){
        this.y = this.down.y - MapPiece.size
        this.map_y = this.down.map_y + 1;
        this.Load()
    }
}

MapPiece.prototype.Show = function()
{
    this.img.style.visibility = 'visible'
}

MapPiece.prototype.Load = function()
{
    // scaled size
    var ssize = (MapPiece.size*this.parent.zoom)

    var xa = Map.base_x + this.map_x*ssize
    var xb = Map.base_x + (this.map_x+1)*ssize
    var ya = Map.base_y + this.map_y*ssize
    var yb = Map.base_y + (this.map_y+1)*ssize

    var url = this.parent.resource.url+'?REQUEST=GetMap&VERSION=1.1.1&SRS=EPSG:23031&Service=WMS'
    url += '&BBOX='+xa+','+ya+','+xb+','+yb
    url += '&WIDTH='+MapPiece.size+'&HEIGHT='+MapPiece.size
    url += '&LAYERS='+this.parent.resource.map_layers
    url += '&STYLES=&FORMAT=JPEG&BGCOLOR=0xFFFFFF&TRANSPARENT=TRUE&EXCEPTION=INIMAGE'
    
    this.img.src = 'wait.gif'
    this.temp.src = url
}

MapPiece.prototype.OnLoad = function()
{
    this.img.src = this.temp.src
}

/*
 * Map Resource
 */
function MapResource(name, url, default_scale, min_scale, max_scale, ini_zoom)
{
    this.name = name
    this.url = url
    this.ini_zoom = ini_zoom?ini_zoom:1
    this.default_scale = default_scale?default_scale:1
    this.min_scale = min_scale?min_scale:1
    this.max_scale = max_scale?max_scale:this.default_scale
    this.layers_names = []
    this.layers = {}
    this.map_layers = ''
}

MapResource.prototype.AddLayer = function(name, layer)
{
    this.layers_names.push(name)
    this.layers[name] = layer
    this.GetLayers()
}

MapResource.prototype.GetLayers = function()
{
    this.map_layers = ''
    for(var i=0; i<this.layers_names.length; i++) {
        this.map_layers += this.layers[this.layers_names[i]]+','
    }
    this.map_layers = this.map_layers.slice(0,-1);
}

/*
 * Map handler
 */

function MapHandler(elem) {
    this.map = new Map(elem)
    this.maps = {}
    this.maps_names = []
    Map.base_x = 429000
    Map.base_y = 4581000
}

MapHandler.prototype.AddMap = function(map) {
    this.maps[map.name] = map
    this.maps_names.push(map.name)
}

MapHandler.prototype.Display = function(mapname) {
    this.map.Display(this.maps[mapname])
}

MapHandler.prototype.Move = function(long, lati)
{
    Map.base_x = long
    Map.base_y = lati
    this.map.Update()
}
